/* main.c */
#include "app_inc.h"

/* Events. */
#define DEMO_SCT_EVENT_IDX_0 0U /* 使用事件(通道)的编号 */
#define DEMO_SCT_EVENT_IDX_1 1U /* 控制输出引脚的事件编号 */

/* Match Registers. */
#define DEMO_SCT_MATCH_REG_0 0U /* 选用SCT_MATCHn寄存器的编号 */
#define DEMO_SCT_MATCH_REG_1 1U

/* States. */
#define DEMO_SCT_STATE_IDX_0 0U /* 使用状态(混合事件)的编号 */

/* Pin Outputs. */
#define DEMO_SCT_PINOUT_IDX_0 0U

/* Application. */
#define DEMO_SCT_PWM_PERIOD_HZ  10000UL /* PWM周期10kHz */
#define DEMO_SCT_PWM_PERIOD_VALUE (BSP_CLK_SYSTEM_CLK_HZ/DEMO_SCT_PWM_PERIOD_HZ)
#define DEMO_SCT_PWM_DUTY_COUNT 100U
#define DEMO_SCT_PWM_DUTY_STEP  (DEMO_SCT_PWM_PERIOD_VALUE / DEMO_SCT_PWM_DUTY_COUNT)

void App_SCT_InitPwmMode(void);
void App_SCT_ConfigPwmPeriod(uint32_t pwmPeriodHz);
void App_SCT_ConfigPwmDuty(uint32_t pwmDutyHz);
void App_SCT_SetPwmDuty(uint32_t pwmDutyHz);
void App_SCT_ConfigPwmOutPin(void);

/*
* 注意：
* SCT使用IO引脚输出信号，产生PWM信号
* 为了产生PWM，SCT使用了双计数器模式
*/

int main(void)
{
    uint32_t pwmDutyPercentage;

    BSP_InitDebugUART(); /* 初始化调试串口 */

    printf("\r\n\r\nSCT_PwmOutput.\r\n");
    printf("Initializing...\r\n");
    
    /* 配置引脚 */
    Chip_SYSCON_EnablePeriphClock(SYSCON_AHBPeriphClk_SWM);
    Chip_SWM_MovablePinAssign(SWM_SCT_OUT0_O, 16); /* 将SCT_OUT_0引脚连接到PIO16 */
    Chip_SYSCON_DisablePeriphClock(SYSCON_AHBPeriphClk_SWM);
    
    /* 打开模块的端口访问时钟 */
    Chip_SYSCON_EnablePeriphClock(SYSCON_AHBPeriphClk_SCT);
    /* 复位SCT模块硬件 */
    Chip_SYSCON_AssertPeriphReset(RESET_SCT);
    Chip_SYSCON_DeassertPeriphReset(RESET_SCT);

    /* 在配置SCT之前停用所有计数器，在每次修改波特率之前均需停用计数器 */
    Chip_SCT_ControlCounter( LPC_SCT,
        SCT_COUNTER_ID_Low16bit_MASK | SCT_COUNTER_ID_High16bit_MASK, /* counterIdMask. */
        eSCT_CounterCtrlCmd_HALT,       /* cmd. HALT命令 */
        1U                              /* param. 置位HALT位以停用计数器 */
    );
    /* 清零计数器 */
    Chip_SCT_ControlCounter( LPC_SCT,
        SCT_COUNTER_ID_Low16bit_MASK | SCT_COUNTER_ID_High16bit_MASK, /* counterIdMask. */
        eSCT_CounterCtrlCmd_CLRCTR,     /* cmd. CLRCTR */
        1U                              /* param. 置位CLRCTR位以清零计数器 */
    );
    /*
    * 配置SCT计数器输出PWM信号
    */
    App_SCT_InitPwmMode();
    App_SCT_ConfigPwmPeriod(DEMO_SCT_PWM_PERIOD_HZ);
    App_SCT_ConfigPwmDuty(100U/2U);
    App_SCT_ConfigPwmOutPin();
    
    printf(" - SCT Ready.\r\n");
    printf(" - All Done.\r\n");
    printf("Input any key to start the counter. ");
    getchar();
    printf("Go.\r\n");

    /* 启动计数器 */
    Chip_SCT_ControlCounter( LPC_SCT,
        SCT_COUNTER_ID_Unify32bit_MASK, /* counterIdMask. */
        eSCT_CounterCtrlCmd_HALT,       /* cmd. HALT命令 */
        0U                              /* param. 清HALT位以启动计数器 */
    );
    
    pwmDutyPercentage = 0U;

    while (1)
    {
        //__WFI();
        getchar();
        if (pwmDutyPercentage > 100U)
        {
            pwmDutyPercentage = 0U;
        }
        
        App_SCT_SetPwmDuty(pwmDutyPercentage);
        pwmDutyPercentage += 10U;
    }
}

/*!
* @brief 配置SCT工作在PWM模式
*
* 配置PWM的公用部分在本函数中设定，例如定时器、Match寄存器等
*/
void App_SCT_InitPwmMode(void)
{
    SCT_CounterConfig_T SctCounterConfigStruct;

    /* 配置计数器 */
    SctCounterConfigStruct.CounterMode = eSCT_CounterMode_32bitx1; /* 使用32bit计数器 */
    SctCounterConfigStruct.ClockSource = eSCT_ClockSource_SystemClock; /* 使用Bus Clock. */
    SctCounterConfigStruct.ClockEdge = eSCT_ClcokEdge_RisingEdgeOnInput0; /* Dummy setting. */
    SctCounterConfigStruct.EnableNoReloadCounterMaskValue = SCT_COUNTER_ID_NONE_MASK; /* 不启用NoLoad功能，保证每次计数初值都是从reload寄存器中载入 */
    SctCounterConfigStruct.SyncInputMaskValue = 0U; /* 不需要启用输入信号同步 */
    SctCounterConfigStruct.EnableAutoLimitCounterMaskValue = SCT_COUNTER_ID_Unify32bit_MASK; /* 周期计数模式，自动回转，只有Match0寄存器支持 */
    Chip_SCT_ConfigCounter(LPC_SCT, &SctCounterConfigStruct);

    /* 配置Match Register/Match Reload 寄存器的工作模式. */
    Chip_SCT_SetEventModes( LPC_SCT,
        SCT_COUNTER_ID_Unify32bit_MASK, /* counterIdMask. */
        (1U << DEMO_SCT_MATCH_REG_0),   /* matchMask. */
        eSCT_EventCounterMode_Match     /* mode. */
    );
}

/*!
* @brief 设定SCT PWM的周期
*
* 使用Event0产生基础周期信号
*/
void App_SCT_ConfigPwmPeriod(uint32_t pwmPeriodHz)
{
    SCT_EventConditionConfig_T SctEventConditionConfigStruct;

    /* 配置Event0，绑定Match0 Register，仅能工作在State0状态下. */
    SctEventConditionConfigStruct.MatchRegIdx = DEMO_SCT_MATCH_REG_0; /* 使用MATCH0寄存器 */
    SctEventConditionConfigStruct.EventCondition = eSCT_EventCondition_MatchOnly; /* 仅在Match条件满足时触发 */
    SctEventConditionConfigStruct.EnableInStatesMaskValue = (1U << DEMO_SCT_STATE_IDX_0); /* 本事件仅可在State 0发生 */
    SctEventConditionConfigStruct.EventIOMode = eSCT_EventIOMode_Output; /* 触发输出 */
    SctEventConditionConfigStruct.EventIOIdx = DEMO_SCT_PINOUT_IDX_0; /* 从Pin0输出 */
    Chip_SCT_ConfigEventCondition(LPC_SCT, DEMO_SCT_EVENT_IDX_0, &SctEventConditionConfigStruct);
    /* Todo: 关于SCT_PINOUT_INX_0在Event0时拉高电平的设定在App_SCT_ConfigPwmOutPin()函数中 */

    /* 预先设定当Event0发生时，回转(Limit)计数器的值 */
    Chip_SCT_PreSetEventLimitCounterCmd( LPC_SCT,
        SCT_COUNTER_ID_Unify32bit_MASK, /* counterIdMask. */
        (1U << DEMO_SCT_EVENT_IDX_0),   /* eventMask. Event0. */
        true                            /* enable. Enable the Limit action. */
    );
    
    /* MATCH寄存器对应PWM的相位，即第一个波形开始输出的时刻 */
    Chip_SCT_SetEventMatchValue( LPC_SCT,
        SCT_COUNTER_ID_Unify32bit_MASK, /* counterIdMask. */
        DEMO_SCT_MATCH_REG_0,           /* matchRegIdx. */
        0U                              /* value. Load value in first time.*/
        );

    /* MATCH RELOAD寄存器的值对应PWM的周期，之后计数器每次达到Match值即重新开始计数 */
    Chip_SCT_SetEventMatchReloadValue( LPC_SCT,
        SCT_COUNTER_ID_Unify32bit_MASK, /* counterIdMask. */
        DEMO_SCT_MATCH_REG_0,           /* matchRegIdx. Match0 Register. */
        DEMO_SCT_PWM_PERIOD_VALUE       /* value. Reload value in future.*/
        );
    
    /* Enable the Event0. */
    Chip_SCT_EnableEvent(LPC_SCT, (1U << DEMO_SCT_EVENT_IDX_0), true);
}

/*!
* @brief 配置SCT PWM的占空比
*
* 使用Event1产生占空比信号，即翻转逻辑输出信号
*
* @param pwmDutyPer PWM占空比
*/
void App_SCT_ConfigPwmDuty(uint32_t pwmDutyPer)
{
    SCT_EventConditionConfig_T SctEventConditionConfigStruct;
    
    /* 配置Event1，绑定Match1 Register，仅工作在State0状态下. */
    SctEventConditionConfigStruct.MatchRegIdx = DEMO_SCT_MATCH_REG_1;
    SctEventConditionConfigStruct.EventCondition = eSCT_EventCondition_MatchOnly; /* 仅在Match条件满足时触发 */
    SctEventConditionConfigStruct.EnableInStatesMaskValue = (1U << DEMO_SCT_STATE_IDX_0); /* 本事件仅可在State 0发生 */
    SctEventConditionConfigStruct.EventIOMode = eSCT_EventIOMode_Output; /* 触发输出 */
    SctEventConditionConfigStruct.EventIOIdx = DEMO_SCT_PINOUT_IDX_0; /* 从Pin0输出 */
    Chip_SCT_ConfigEventCondition(LPC_SCT, DEMO_SCT_EVENT_IDX_1, &SctEventConditionConfigStruct);
    /* Todo: 关于SCT_PINOUT_INX_0在Event0时拉低电平的设定在App_SCT_ConfigPwmOutPin()函数中 */

#if 0 /* Use App_SCT_SetPwmDuty() instead. */
    /* Match Register 1 is used to generate the duty cycle. */
    Chip_SCT_SetEventMatchReloadValue( LPC_SCT,
        SCT_COUNTER_ID_Unify32bit_MASK, /* counterIdMask. */
        DEMO_SCT_MATCH_REG_1,           /* matchRegIdx. */
        (pwmDutyPer * DEMO_SCT_PWM_DUTY_STEP)  /* value. Reload value in future.*/
        );
#endif
    App_SCT_SetPwmDuty(pwmDutyPer);
    
    /* Enable the Event1. */
    Chip_SCT_EnableEvent(LPC_SCT, (1U << DEMO_SCT_EVENT_IDX_1), true);
}

void App_SCT_SetPwmDuty(uint32_t pwmDutyPer)
{
    /* Match Register 1 is used to generate the duty cycle. */
    Chip_SCT_SetEventMatchReloadValue( LPC_SCT,
        SCT_COUNTER_ID_Unify32bit_MASK, /* counterIdMask. */
        DEMO_SCT_MATCH_REG_1,           /* matchRegIdx. */
        (pwmDutyPer * DEMO_SCT_PWM_DUTY_STEP)  /* value. Reload value in future.*/
        );
}

/*!
* @brief 配置SCT PWM的输出信号，同时设定PWM信号占空比
*/
void App_SCT_ConfigPwmOutPin(void)
{
    /* 当Event0到来时，在Pin0输出逻辑1; 当Event1到来时，在Pin0输出逻辑0.
    */
    Chip_SCT_PreSetEventSetOutputCmd(LPC_SCT, DEMO_SCT_PINOUT_IDX_0, 1U<<DEMO_SCT_EVENT_IDX_0, true);
    Chip_SCT_PreSetEventSetOutputCmd(LPC_SCT, DEMO_SCT_PINOUT_IDX_0, 1U<<DEMO_SCT_EVENT_IDX_1, false);
    Chip_SCT_PreSetEventClearOutputCmd(LPC_SCT, DEMO_SCT_PINOUT_IDX_0, 1U<<DEMO_SCT_EVENT_IDX_0, false);
    Chip_SCT_PreSetEventClearOutputCmd(LPC_SCT, DEMO_SCT_PINOUT_IDX_0, 1U<<DEMO_SCT_EVENT_IDX_1, true);
    
    /* 这部分配置在本工程中没有意义，不过在更复杂的工程中可能会用到这两个API */
#if 0
    Chip_SCT_SetOutputConflictSolution(LPC_SCT, DEMO_SCT_PINOUT_IDX_0, eSCT_OutputConflictResolution_Set);
    Chip_SCT_SetOutputModeInBiDirCounting(LPC_SCT, DEMO_SCT_PINOUT_IDX_0, eSCT_OutputModeInBiDirCounting_Normal);
#endif
}

/* EOF. */

